試分析下列程式碼:
#include <iostream>
int main()
{
unsigned int a = 6;
int b = -20;
(a + b > 6) ? puts(">6") : puts("<=6");
return 0;
}
這裡有個 unsigned int 的變數 a,值為 6;另外有個 int 變數 b,值為 -20,那兩者相加是否大於 6 呢?
-20 + 6 = -14,而 -14 小於 6,所以在三元運算式中,應該會顯示 <=6 對吧?那實際上顯示的是什麼呢?
>6
為什麼?!
這關鍵就在於 6 跟 -20 兩者的資料型別:
6 是 unsigned int。-20 是 int。6 比較簡單,轉成二進制就是 0110。
-20 因為帶有負號,轉二進制就比較複雜了,會用到「二補數(Two's Complement)」,其步驟如下(以 8 位元為例):
20:0000 0000 0001 0100
1111 1111 1110 1011
1111 1111 1110 1100
這串 1111 1111 1110 1100 在二補數時,最高位元 1 代表負號,因此轉換成十進制時為:
1 1 1 1 1 1 1 1 1 1 1 0 1 1 0 0
-32768 + 16384 + 8192 + 4096 + 2048 + 1024 + 512 + 256 + 128 + 64 + 32 + 0 + 8 + 4 + 0 + 0 = -20
但當 unsigned int 跟 int 相加時,有號數 int 會被默默地轉成無號數 unsigned int,也就是說最高位元 1 不再代表 -32768,而是 +32768。
所以同樣是 1111 1111 1110 1100,在十進制中就變成:
1 1 1 1 1 1 1 1 1 1 1 0 1 1 0 0
32768 + 16384 + 8192 + 4096 + 2048 + 1024 + 512 + 256 + 128 + 64 + 32 + 0 + 8 + 4 + 0 + 0 = 65516
所以 65516(二進制為 1111 1111 1110 1100)跟 6(二進制為 0000 0000 0000 0110)相加後:
1111 1111 1110 1100 <- 65516 轉十進制
+ 0000 0000 0000 0110 <- 6 轉十進制
----------------------
1111 1111 1111 0010 <- 十進制為 65522
最終結果為 65522,因此 >6 成立。
在寫這篇文章時,我不禁好奇為何要有這樣的機制?為什麼是 int 改成 unsigned int,而不是 unsigned int 轉成 int?
在 C++ 中,如果有不同資料型別混搭,編譯器會默默把位元數較低者轉為位元數較高者的資料型別,其順序由少位元到多位元如下:
(最少位元)bool -> char -> short -> int -> long -> long long -> float -> double -> long double(最多位元)
讓較少位元者擴展為較多位元者,可讓資料型別對齊時,確保含有較多位元資訊者資料不流失。
然而,int 跟 unsigned int 位元數量相同,又是怎麼決定誰轉成誰的呢?
在 C++ 中,兩資料型別範圍如下:
int:-2,147,483,648 到 2,147,483,647。unsigned int:0 到 4,294,967,295。所以如果有一個數是 3,000,000,000,這個數在 unsigned int 過得很好,但如果硬轉成有號整數 int,會直接超過 int 的容許範圍、直接爆掉;反之,任何在 int 範圍中的數,在轉為 unsigned int 後,都還是能穩穩地保持在 unsigned int 的容許範圍。